package com.siping.算法.权重轮询;

import java.util.ArrayList;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap;
import java.util.concurrent.ThreadLocalRandom;

/**
 * @author YangXu
 * @date 2022/10/7
 * @description:
 */
public class RoundRobinWeightLoadBalance {
    public static List<Refer> refers = new ArrayList<>();
    private static final ConcurrentMap<String, AtomicPositiveInteger> sequences = new ConcurrentHashMap<>();
    private static int[] weights = new int[] {8, 2, 1};
    private static String[] names = new String[] {"A", "B", "C"};

    static {
        for (int i = 1; i < 4; i++) {
            Refer refer = new Refer();
            refer.setServerName(names[i-1]);
            refer.setActiveRefererCount(ThreadLocalRandom.current().nextInt(11));
            refer.setAvailable(ThreadLocalRandom.current().nextInt(2) == 1 ? true : false);
            refer.setMethod("sayHello");
            refer.setServiceKey("com.zzx.DemoService");
            refer.setWeight(weights[i-1]);
            refers.add(refer);
        }
    }

    private static Refer roundRobinWeight() {
        String key = refers.get(0).getServiceKey() + "." + refers.get(0).getMethod();
        int length = refers.size();
        //最大权重
        int maxWeight = 0;
        //最小权重
        int minWeight = Integer.MAX_VALUE;
        final LinkedHashMap<Refer, IntegerWrapper> referToWeightMap = new LinkedHashMap<>();
        //权重总和
        int weightSum = 0;

        //下面这个循环主要用于查找最大和最小权重，计算权重总和
        for (int i = 0; i < length; i++) {
            int weight = refers.get(i).getWeight();
            //获取权重最大和最小值
            maxWeight = Math.max(maxWeight, weight);
            minWeight = Math.min(minWeight, weight);
            if(weight > 0) {
                referToWeightMap.put(refers.get(i), new IntegerWrapper(weight));
                //累加权重
                weightSum += weight;
            }
        }

        //获取当前服务对应的调用序列对象 AtomicPositiveInteger,默认为0
        AtomicPositiveInteger sequence = sequences.get(key);
        if(sequence == null) {
            sequences.putIfAbsent(key, new AtomicPositiveInteger());
            sequence = sequences.get(key);
        }

        //获取当前的调用编号
        int currentSequence = sequence.getAndIncrement();
        //如果最小权重小于最大权重，表明服务提供者之间的权重是不相等的
        if(maxWeight > 0 && minWeight < maxWeight) {
            //使用调用编号对权重总和进行取余操作
            int mod = currentSequence % weightSum;
            //进行maxWeight次遍历
            for(int i = 0; i < maxWeight; i++) {
                //遍历 invokerToWeightMap
                for (Map.Entry<Refer, IntegerWrapper> each :  referToWeightMap.entrySet()) {
                    final Refer k = each.getKey();
                    //获取权重包装类数据
                    final IntegerWrapper v = each.getValue();

                    //如果 mod = 0, 且权重大于0， 此时返回相应的Invoker
                    if(mod == 0 && v.getValue() > 0) {
                        return k;
                    }

                    //mod != 0,且权重大于0,此时权重和mod分别进行自减操作
                    if(v.getValue() > 0) {
                        v.decrement();
                        mod--;
                    }
                }
            }
        }

        return refers.get(currentSequence%length);
    }

    private static final class IntegerWrapper {
        private int value;

        public IntegerWrapper(int value) {
            this.value = value;
        }

        public void decrement(){
            this.value--;
        }

        public int getValue() {
            return value;
        }

        public void setValue(int value) {
            this.value = value;
        }
    }

    public static void main(String[] args) {
        for (Refer refer : refers) {
            System.out.println(refer);
        }

        System.out.println("---------------------------------------------");
        for (int i = 0; i < 8; i++) {
            System.out.println("获取按权重轮询Refer:" + roundRobinWeight());
        }
    }
}
